home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr01
/
jock.zip
/
TOTDOC11.ZIP
/
CHAPT9.TXT
< prev
next >
Wrap
Text File
|
1991-02-11
|
86KB
|
2,168 lines
Managing
Lists
"Three questions are essential... What is the author's object? How far
has he accomplished it? How far is that object worthy of approbation?
Nathanial P Willis
Introduction
Lists, lists, lists. Our lives and our software are often dominated by
lists. The Toolkit provides three powerful units to help you manage
software lists. (Your lives are your own responsibility!)
In this chapter, the totLINK and totLIST units are discussed in detail.
totLINK provides objects to help you maintain and sort large lists, and
totLIST provides objects for displaying lists and arrays in windows.
A common form of software list is a file directory list. The Toolkit
provides two flexible directory displaying objects, and these are dis-
cussed in the next chapter.
Array and Linked List Theory
One-dimensional arrays provide a very quick and convenient way of
managing lists. An array is a data structure designed to store data
about a fixed number of identical elements. For example, the following
variable could be used to store the address of my two sisters' and
three brothers' addresses:
Addresses: array [1..5] of string;
Data in the array is accessed by specifying the element number. For
example, the following statement would update my second sister's
address:
Addresses[2] := "38 The Ridgeway, Friern Barnet, London"
Arrays are very easy to create, but suffer from two major weaknesses:
q The size of an array is fixed at compile time. In other words, in
your source code you must explicitly state the size (or bounds)
of the array. In our simple example, this was not a problem
because I have only five siblings. Imagine, however, that you
need to write a generic name and address program. You don't know
how many names and addresses will be required, but you must spec-
ify the number in your program. The typical solution is to choose
a large number of elements (e.g. 200) and hope that the user
doesn't need more. Not only does this set a limit on the pro-
gram's capacity, it also wastes memory if the maximum number of
9-2 User's Guide
--------------------------------------------------------------------------------
elements are not used. Turbo Pascal automatically allocates the
memory for the entire array regardless of how many elements are
actually used. Which leads us to the second shortcoming.
q The maximum size of a program's data segment in Turbo Pascal is
64k. This means that the sum of all the program's global vari-
ables cannot exceed 64k, and, therefore, no individual data
structure can exceed 64k. In our example, each element of the
array consumed 256 bytes, so 256 of these elements would consume
the entire data segment leaving no room for other global vari-
ables.
Linked lists do not suffer from these shortcomings. With a linked list,
each element is stored independently. The first element in the list
includes a pointer indicating where the second element in the list is
stored. The second element in the list includes a pointer to the loca-
tion of the third element in the list, and so on. When you reach an
element in the list that points to NIL, you know you have reached the
end of the list. You do not need to fix the maximum number of elements
in the list at compile time. Furthermore, the data is stored on the
heap (not in the data segment) and so is not limited to 64k. The list
can use all of the available memory.
Although linked lists are very flexible, they can prove complicated to
program and manage. Fortunately, the Toolkit provides a very powerful
object which makes managing linked lists a breeze.
Managing Linked Lists
The totLINK unit includes the object DLLOBJ. This object provides a
variety of methods for creating and managing linked lists. If you are
interested, DLLOBJ actually maintains a doubly-linked list. Each ele-
ment in the list points to the next element and the previous element.
This is more efficient when you need to move up and down the list.
The DLLOBJ object is designed to store raw data, i.e. untyped vari-
ables. This allows you to store literally any data in a DLLOBJ object.
It is important to note that DLLOBJ is an abstract object and you
should not create any instances of type DLLOBJ.
In this chapter, we will concentrate on a descendant object, StrDLLOBJ,
designed specifically to manage lists of strings. If you want to create
objects which manage other data types, all you have to do is create an
object descended from DLLOBJ. In part 2: Extending the Toolkit, there
is a chapter describing how to create other descendants of DLLOBJ to
manage specific data types. By way of an example, it describes how to
create a linked list object for managing records.
Managing Lists 9-3
--------------------------------------------------------------------------------
Before exploring the StrDLLOBJ methods, you need to understand some
doubly-linked-list jargon. Each element in the list is referred to as a
node. Behind the scenes, the Toolkit keeps track of the location of the
first node in the list, the last node in the list, and the node that
was last accessed. The last-accessed node is referred to as the active
node.
Creating a List
To create a linked list, you must declare an instance of type StrDLLOBJ
and then call the Init and Add methods. The syntax of these methods is
as follows:
Init;
This method initializes the object and must be called before the other
methods.
Add(Str:string): integer;
This function method creates a new node in the linked list and stores
it in the string Str. The node is actually created immediately follow-
ing the active node. Once the node has been added, the active node is
moved to point to it. The function returns an integer code - a 0
indicates success, a 1 indicates there was insufficient memory to
create the node, and a 2 indicates there was enough memory to create
the node, but too little memory to store the string.
Ordinarily, to populate a list, all you have to do is call Init, and
then make multiple calls to Add. Listed below is an extract of the demo
program, DEMLK1.PAS, which stores a list of names in a linked list.
var
GirlFriends: StrDLLOBJ;
Retcode: integer;
begin
with GirlFriends do
begin
Init;
Retcode := Add('Erica');
if Retcode <> 0 then exit;
Retcode := Add('Theresa');
if Retcode <> 0 then exit;
Retcode := Add('Lynn');
if Retcode <> 0 then exit;
Retcode := Add('Donna');
if Retcode <> 0 then exit;
9-4 User's Guide
--------------------------------------------------------------------------------
Retcode := Add('Godzilla');
if Retcode <> 0 then exit;
{....}
end;
end.
To save checking the value of Retcode every time you add a node, you
might check that there is enough memory available for all the nodes,
and ignore the return code, as follows:
var
GirlFriends: StrDLLOBJ;
Retcode: integer;
begin
with GirlFriends do
begin
Init;
if maxavail > 200 then
begin
Retcode := Add('Erica');
Retcode := Add('Theresa');
Retcode := Add('Lynn');
Retcode := Add('Donna');
Retcode := Add('Godzilla');
{...}
end;
end;
end.
In fact, if you are using Turbo Pascal 6.0 and the extended compiler
directive {$X+} has been set, you can ignore the function return value,
as follows:
{$X+}
var
GirlFriends: StrDLLOBJ;
begin
with GirlFriends do
begin
Init;
if maxavail > 200 then
begin
Add('Erica');
Add('Theresa');
Add('Lynn');
Add('Donna');
Add('Godzilla');
Managing Lists 9-5
--------------------------------------------------------------------------------
{...}
end;
end;
end.
Maintaining a List
Once you have created a list, the following methods can be used to
navigate the list:
TotalNodes: longint;
This function method returns a longint identifying the total number of
nodes in the list.
ActiveNodeNumber: longint;
Returns the position of the active node.
Jump(NodeNumber: longint)
This method moves the ActiveNodePtr to the specified node number. Add
inserts a node after the active node pointer. So, to insert a node
anywhere in the list, call the method Jump to move the active node
pointer and then call Add.
Advance(Amount: longint);
Moves the active node down the list the specified number of nodes. If
there are insufficient nodes, the active node pointer is moved to the
end of the list.
Retreat(Amount: longint);
Moves the active node up the list the specified number of nodes. If
there are too few nodes, the active node pointer is moved to the begin-
ning of the list.
Advanced List Management
In many programs, all you'll need to do is create a list, add items to
the list, and then display the list using one of the browse or list
displaying objects (discussed later). However, if you want to delete
items from the list, swap the positions of two items in the list, or
change an item in the list, you will need to access the nodes in the
list directly.
9-6 User's Guide
--------------------------------------------------------------------------------
Each node in the list is actually a DLLNodeOBJ object, and many of the
list manipulation methods access a pointer to the node known as a
DLLNodePtr.
Without worrying about the details, all you need to know is how to
determine the value of DLLNodePtr for any given node. The following
four function methods serve this purpose:
NodePtr(NodeNumber:longint): DLLNodePtr;
Pass this function a node number, and it will return a pointer to the
node.
ActiveNodePtr: DLLNodePtr;
Returns a pointer to the active node.
StartNodePtr: DLLNodePtr;
Returns a pointer to the first node in the list.
EndNodePtr: DLLNodePtr;
Returns a pointer to the last node in the list.
Having established the DLLNodePtr value, the following methods can be
used to manipulate the node data:
Change(Node:DLLNodePtr;Str:string):integer;
Changes the value at the node to Str. This function method returns an
integer to indicate whether the change was a success: 0 indicates suc-
cess, 2 indicates insufficient memory for the data, and 3 indicates an
invalid node pointer.
DelNode(Node:DLLNodePtr);
Deletes the node.
SwapNodes(Node1,Node2:DLLNodePtr);
Swaps the data assigned to the two nodes.
InsertBefore(Node:DLLNodePtr; Str:string);
Managing Lists 9-7
--------------------------------------------------------------------------------
Inserts a new node immediately before the specified node. (The Add
method inserts the data after the active node.) This method provides a
way of inserting a node at the very beginning of the list. After this
method has been called, the active node pointer is set to the first
node in the list.
GetStr(Node:DLLNodePtr; Start,Finish:longint): string;
This function returns a sub-string of the data stored at the specified
node. The parameters Start and Finish indicate the starting and ending
characters to be extracted in the string. For example, values of 6 and
10 will return five characters commencing with the character in posi-
tion 6 of the string. Pass values of zero for the Start and Finish
parameters, if you want the entire string.
Sort(SortID: shortint; Ascending: boolean);
This powerful method rearranges the order of all the nodes in a list,
i.e. it sorts the list. The sort routine is generic and can be utilized
by any descendant of the DLLOBJ object. In the case of StrDLLOBJ, the
first parameter has no meaning, and should be set to one. If you want
the list sorted in ascending order (i.e. lowest first), pass TRUE as
the second parameter, otherwise pass FALSE.
Listed below is the demo program DEMLK2.PAS which illustrates many of
the methods discussed thus far. Figure 9.1 shows the generated output.
Program DemoLinkTwo;
{DEMLK2 - creating a linked list}
Uses CRT,
totLink;
var
GirlFriends: StrDLLOBJ;
Retcode: integer;
I : integer;
begin
ClrScr;
with GirlFriends do
begin
Init;
if maxavail > 200 then
begin
Retcode := Add('Erica');
Retcode := Add('Theresa');
Retcode := Add('Lynn');
Retcode := Add('Donna');
Retcode := Add('Godzilla');
9-8 User's Guide
--------------------------------------------------------------------------------
writeln('Total nodes: ',TotalNodes);
writeln('Active node number: ',ActiveNodeNumber);
for I := 1 to TotalNodes do
writeln(GetStr(NodePtr(I),0,0));
writeln;
RetCode := InsertBefore(NodePtr(4),'Joyce');
writeln('Total nodes: ',TotalNodes);
writeln('Active node number: ',ActiveNodeNumber);
SwapNodes(NodePtr(5),NodePtr(2));
Retcode := Change(StartNodePtr,'Ekka');
for I := 1 to TotalNodes do
writeln(GetStr(NodePtr(I),0,0));
writeln;
writeln('Sorted:');
Sort(1,true);
for I := 1 to TotalNodes do
writeln(GetStr(NodePtr(I),0,0));
end;
end;
end.
Figure 9.1 [SCREEN]
Using
StrDLLOBJ
Removing a List
Calling the method Done will always dispose of all the memory consumed
by a list. A list can also be emptied with the following method:
EmptyList;
Removes all the memory allocated on the heap by chaining back through
the list and disposing of each node. The ActiveNodePtr, StartNodePtr
and EndNodePtr are set to nil, and TotalNodes is set to nil.
Comparing StrDLLOBJ to Arrays
If you have never used linked lists before, but you have used arrays,
you might be interested in comparing array commands with StrDLLOBJ com-
mands. Table 9.1 compares the command syntax by manipulating a list of
5 names.
StrDLLOBJ String Array
Managing Lists 9-9
--------------------------------------------------------------------------------
Declaring the Variable or Instance
var NameList: StrDLLOBJ; var NameList: array[1..5] of
string;
Initializing the List
NameList.Init; fillchar(NameList,sizeof(NameLis
t),#0);
Populating the List
with NameList do NameList[1] := 'Mark';
begin NameList[2] := 'Kirk';
Retcode := Add('Mark'); NameList[3] := 'Lloyd';
Retcode := Add('Kirk'); NameList[4] := 'Charles';
Retcode := Add('LLoyd'); NameList[5] := 'Keith';
Retcode := Add('Charles');
Retcode := Add('Keith');
end;
Writing an item to the display
with NameList do writeln(NameList[3]);
writeln(GetStr(NodePtr(3),0,0));
Swapping two items
with NameList do Temp := NameList[4];
SwapNodes(NodePtr(4),NodePtr(5)); NameList[4] := NameList[5];
NameList[5] := Temp;
Sorting the List
NameList.Sort(1,true); Ugh!
Erasing the Entries
NameList.EmptyList; fillchar(NameList,sizeof(NameLis
t),#0);
Don't forget that the StrDLLOBJ is much more memory efficient, and is
not limited to a set number of elements.
Much of the power of DLLOBJ is not realized until you create descendant
objects, customized for your specific needs. Refer to Part 2: Extending
The Toolkit for further details.
Browse Lists
Whether your data is in a DLLOBJ (or descendant) object, or in a string
array, you will want to take advantage of the Toolkit's objects for
9-10 User's Guide
--------------------------------------------------------------------------------
automatically displaying lists in windows. In this section, the Brow-
seOBJ family of objects will be discussed. These objects display lists
in a scrollable window.
Note that a browse window does not include a highlight bar. The
user browses the contents of the list, but does not select an
individual element. The ListOBJ family (discussed in the next sec-
tion) provides objects for highlighting individual elements in a
list.
Figure 9.2 shows the object hierarchy for the BrowseOBJ family. The
base object BrowseOBJ is abstract, and you should not create any
instances of this type. The BrowseArrayOBJ object displays the contents
of a string array, and the BrowseLinkOBJ displays the contents of an
object in the DLLOBJ family. Many programs need to display the contents
of ASCII files, and the object BrowseFileOBJ makes this a snap.
Figure 9.2 [PICTURE]
BrowseOBJ
Object Hierarchy
Common Methods
The totLOOK unit includes an object LookTOT which is used to control
the overall look and feel of applications developed with the Toolkit.
Call the method LookTOT^.SetListKeys to change the keys which the user
can press to close the browse window. The defaults are [KEYCAP] and
[KEYCAP]. Refer to page 3-12 for further details.
Because all the objects are derived from the BrowseOBJ, they share a
set of common methods. The following methods can be used with any of
the browse objects:
SetTopPick(TopPick: longint);
This method identifies the node number of the first node that will be
displayed in the list window.
SetStartCol(Column: longint);
This method identifies the character position of the first character of
the string which is visible in the list window.
Managing Lists 9-11
--------------------------------------------------------------------------------
SetEndCol(Column: longint);
This method sets the maximum lateral scrolling by identifying the last
column which will be displayed when the window is scrolled to the right
maximum. All lateral scrolling can be disabled by calling the Stretch-
WinOBJ method SetScrollable (discussed later).
Show;
This method displays the list and immediately returns control back to
the calling procedure, i.e. the Toolkit does not wait for user input.
This option is useful if you want to display the list on the screen,
but not activate it.
Go;
This method is the main "do it" method. If the list is not on display,
it is drawn. The Toolkit then processes user input until an end key is
pressed.
LastKey:word;
LastKey returns the key code of the key the user pressed to end the
browse session. This is used if you need to know whether the user
escaped or pressed the finish key.
Done;
As always, Done disposes of the Browse window object, and should always
be called when the object is no longer required.
Modifying the Window Characteristics
The list data is displayed in a window of type StretchWinOBJ. You can
modify any window attribute by directly calling the StretchWinOBJ meth-
ods. BrowseOBJ includes a function method, Win, which returns a pointer
to the StretchWinOBJ instance. To modify the window, call the
appropriate StretchWinOBJ method using the following syntax:
[BrowseOBJInstance].Win^.method
For example, the following statements would change the default window
size, style and title:
var MyBrowser: BrowseLinkOBJ;
begin
with MyBrowser do
begin
9-12 User's Guide
--------------------------------------------------------------------------------
Init;
Win^.SetSize(1,1,50,20,3);
Win^.SetTitle('My Browser');
end;
end.
All the display colors used by the browse objects are controlled by the
underlying window attributes. To change the browse display colors call
the window method SetColors, e.g.
Win^.SetColors(23,23,31,30);
Call Win^ methods to set all the browse window characteristics, e.g.
colors, title, remove status, boundaries, etc. Refer to chapter 7:
Using Windows for a thorough discussion of the StretchWinOBJ methods.
Browsing Arrays
The BrowseArrayOBJ object provides a very simple way to browse the
contents of a string array. In addition to the common methods just
described, there is another method as follows:
AssignList(var StrArray; Total:longint; StrLength:byte);
AssignList identifies the string array that will be displayed. The
three parameters are the string array, the total number of elements in
the array, and the length of each string in the array. The string
length parameter must reflect the string length of the array when it
was declared, not the maximum length of any string assigned to the
array.
For example, if a string array was declared as follows:
MyGirls: array[1..54] of string;
then the list is assigned with the following calls:
with MyBrowser do
begin
Init;
AssignList(MyGirls,54,255);
{...}
end;
Managing Lists 9-13
--------------------------------------------------------------------------------
Having assigned the list, all you need to do is call Show and the
browse window will pop onto the display. Listed below is a the demo
program DEMBR1, followed by figure 9.3 illustrating the resultant dis-
play.
Program DemoBrowseArrayOne;
{SEMBR1}
Uses DOS,CRT,
totINPUT, totFAST,totLIST, totSTR;
var
BWin: BrowseArrayOBJ;
StringList: array[1..26] of string[100];
I : integer;
begin
for I := 1 to 26 do {first assign something to the string array}
StringList[I] := 'Line '+
IntToStr(I)+': '+
replicate(80,char(I+64));
Screen.Clear(white,'░'); {paint the screen}
Key.SetFast;
Key.SetClick(true);
with BWin do
begin
Init;
AssignList(StringList,26,100);
Go;
Done;
end;
end.
Figure 9.3 [SCREEN]
Browsing an
Array
The demo program DEMBR2.PAS, listed below, is very similar to
DEMBR1.PAS. The only difference is that two Win^ methods are called to
modify the default window settings. Figure 9.4 shows the impact on the
display.
Program DemoBrowseArray;
Uses DOS,CRT,
totFAST,totLIST, totSTR;
9-14 User's Guide
--------------------------------------------------------------------------------
var
BWin: BrowseArrayOBJ;
StringList: array[1..26] of string[100];
I : integer;
begin
for I := 1 to 26 do {first assign something to the string array}
StringList[I] := 'Line '+
IntToStr(I)+': '
+replicate(80,char(I+64));
Screen.Clear(white,'░'); {paint the screen}
with BWin do
begin
Init;
AssignList(StringList,26,100);
Win^.SetSize(30,5,50,15,2);
Win^.SetTitle('Array Browse Demo');
Go;
Done;
end;
end.
Figure 9.4 [SCREEN]
Modifying the
Browse Window
Browsing Linked Lists
The BrowseLinkOBJ is very similar to the BrowseArrayOBJ, the primary
difference being that BrowseLinkOBJ displays the data stored in a
DLLOBJ object rather than in an array.
In addition to the common methods described, there are two additional
methods as follows:
AssignList(Var LinkList: DLLOBJ);
This method is passed a DLLOBJ instance, or any instance of an object
derived from DLLOBJ, e.g. StrDLLOBJ.
ListPtr: DLLPtr;
This method returns a pointer to the DLLOBJ used to create the list.
You can access the linked list by using the syntax MyBrow-
ser.ListPtr^.method.
Listed below is the program DEMBR3.PAS, which illustrates how to build
a large list of 500 items and display it in a browse window. Figure 9.5
shows the browse display.
Managing Lists 9-15
--------------------------------------------------------------------------------
Program DemoBrowseList;
Uses DOS,CRT,
totFAST, totLINK, totLIST, totSTR;
var
BWin: BrowseLinkOBJ;
LL : StrDLLOBJ;
procedure CreateLinkedList;
{}
var
I, Retcode : integer;
begin
with LL do
begin
Init;
for I := 1 to 500 do
Retcode := Add('This is line '+IntToStr(I)+
': '+replicate(200,char(random(255))));
end; {with}
end; {CreateLinkedList}
begin
Screen.Clear(white,'░'); {paint the screen}
CreateLinkedList;
with BWin do
begin
Init;
AssignList(LL);
Win^.SetTitle('List Browse Demo');
Go;
Done;
end;
LL.Done
end.
Figure 9.5 [SCREEN]
Browsing a
StrDLLOBJ
Browsing Files
The BrowseFileOBJ object makes browsing ASCII text files easy. Browse-
FileOBJ is descended from BrowseOBJ and inherits all the associated
methods. In addition, the following method AssignFile is used to
instruct the object on which file to display:
9-16 User's Guide
--------------------------------------------------------------------------------
AssignFile(Filename:string): integer;
This function method is passed a string indicating the name of the file
to be browsed. Behind the scenes, this function loads the file from
disk into a temporary StrDLLOBJ object. The function will return a 0 if
the file was successfully loaded. A return value of 1 indicates that
the file was not found, and so cannot be displayed. A return value of 2
indicates that there wasn't enough memory to load the entire file. In
such an instance you may want to use a MessageOBJ to advise the user
that only part of the file can be browsed.
That's all there is to it! Just declare a BrowseFileOBJ instance and
call the methods Init, AssignFile and Go:
var FileWin: BrowseFileOBJ;
begin
with FileWin do
begin
Init;
if AssignFile('Help.TXT') = 0 then
Go;
Done;
end;
end.
The following method can be used to directly access the linked list
created by the BrowseFileOBJ instance:
ListPtr: StrDLLPtr;
This method returns a pointer to the StrDLLOBJ used to create the list.
You can access the linked list by using the syntax MyFile.ListPtr^.me-
thod.
Listed below is the example program, DEMBR4.PAS. This small program
will display the contents of either a file specified on the command
line when the program was executed, or (if no parameters were speci-
fied) the contents of c:\autoexec.bat.
Program DemoBrowseFile;
Uses DOS,CRT,
totFAST, totINPUT, totLINK, totLIST, totSTR;
const
DefaultFile = '\autoexec.bat';
var
Managing Lists 9-17
--------------------------------------------------------------------------------
BWin: BrowseFileOBJ;
RetCode: integer;
Filename: string;
begin
Screen.Clear(white,'░'); {paint the screen}
if ParamCount = 0 then
FileName := DefaultFile
else
FileName := ParamStr(1);
with BWin do
begin
Init;
Retcode := AssignFile(Filename);
if Retcode in [0,2] then
Go
else
Writeln('Unable to load file: ',Filename,'.');
Key.DelayKey(2000);
Done;
end;
end.
9-18 User's Guide
--------------------------------------------------------------------------------
Selection Lists
So far, this chapter has focused on browse windows, where the user can
simply scroll data vertically and horizontally. By contrast, a list
window is designed to display selectable items, much like a menu. One
of the items within the list window is always highlighted, and the list
may be organized into single or multiple columns. The scroll bars are
used to move the item highlight bar. The list is removed when the user
presses an end key such as [KEYCAP] or [KEYCAP], when the user presses
[KEYCAP], or when the user double-clicks the mouse on an item.
When appropriate, users may even select, or tag, multiple items from
the list, and each selected item is marked with a special character.
The tag status of the highlighted option can be toggled by hitting the
[KEYCAP]. All items can be tagged or untagged at once by pressing [KEY-
CAP] and [KEYCAP], respectively.
Figure 9.6 shows a typical list window.
Figure 9.6 [SCREEN]
A Typical List
Window
By default, a list window is stretchable, and if the user changes the
dimensions, the Toolkit adjusts the displayed columns and rows. Figure
9.7 is the same List window as shown in figure 9.6, after the user has
stretched the window.
Figure 9.7 [SCREEN]
A Stretched List
Window
The primary Toolkit object for displaying lists in a window is ListOBJ,
but this is an abstract object which should not be instanciated. Lis-
tOBJ has a number of descendants designed to display different data
types in lists. Figure 9.8 illustrates the ListOBJ object hierarchy.
The object hierarchy is similar in principle to the BrowseOBJ hierar-
chy, with the following main objects:
ListArrayOBJ Displays a list derived from a string array.
ListLinkOBJ Displays a list derived from any DLLOBJ object or
descendant.
ListDirOBJ & These two objects are used for displaying directories
ListDirSortOBJ in a list window and are discussed in the next chapter.
Managing Lists 9-19
--------------------------------------------------------------------------------
Figure 9.8 [PICTURE]
ListOBJ
Object Hierarchy
Common Methods
LookTOT (in the unit totLook) is used to control the overall look and
feel of applications developed with the Toolkit. As well as the window-
related methods, the following two LookTOT methods impact all list
objects:
SetListKeys Identifies the keys to globally tag or untag windows,
and close the list display.
SetListChars Identifies the characters which will highlight the
active item, as well as the characters which identify
the tagged and non-tagged items.
Refer to page 3-12 for further details.
Because all the list objects are derived from the ListOBJ, they share a
set of common methods. The following methods can be used with any of
the list objects:
SetTopPick(TopPick: longint);
This method identifies the node number of the first node that will be
displayed in the list window, i.e. the first visible item in the list.
By default, this is the first item in the list.
SetActivePick(ThePick:longint);
The highlighted item is referred to as the active pick. This procedure
is used to set which item (or pick) will be highlighted when the list
is first displayed. The passed parameter represents the number of the
item in the visible window, with the top left item having a value of
one. For example, if the top pick (the first visible pick) was item
number 12 and you wanted to highlight the 15th item, pass a value of
four. This will instruct the Toolkit to highlight the fourth visible
pick. The default is the top pick. If an invalid value is used, e.g.
the value is greater than the number of visible picks, the value is set
to the top pick.
SetTagging(On:boolean);
This method is used to identify whether the user will be allowed to tag
and untag items. Pass true to enable item tagging.
9-20 User's Guide
--------------------------------------------------------------------------------
SetColWidth(Wid:byte);
This sets the width of each column in characters. If the column width
is set to zero, the list will be displayed in a single column only, and
the column width will be as wide as the window. When setting the column
width, remember to allow for the additional characters used to high-
light the active pick, and to signify tagged and non-tagged items.
Show;
This method displays the list and immediately returns control to the
calling procedure, i.e. the Toolkit does not wait for user input. This
option is useful if you want to display the list on the screen, but not
activate it.
Go;
This method is the main "do it" method. If the list is not on display,
it is drawn. The Toolkit then processes user input until an end key is
pressed.
GetHiString:string;
This function returns a string representing the highlighted item's
data.
LastKey:word;
LastKey returns the key code of the key the user pressed to end the
browse session. This is used if you need to know whether the user
escaped or pressed the done key.
Done;
As always, Done disposes of the Browse window object, and should always
be called when the object is no longer required.
Modifying Window Characteristics
Like BrowseOBJ, the list data is displayed in a window of type Stretch-
WinOBJ. You can modify any window attribute by directly calling the
StretchWinOBJ methods. ListOBJ includes a function method, Win, which
returns a pointer to the StretchWinOBJ instance. To modify the window,
call the appropriate StretchWinOBJ method using the following syntax:
[ListOBJInstance].Win^.method
For example, the following statements would change the default window
size, style and title:
Managing Lists 9-21
--------------------------------------------------------------------------------
var MyLister: ListLinkOBJ;
begin
with MyList do
begin
Init;
Win^.SetSize(1,1,50,20,3);
Win^.SetTitle('My Lister');
end;
end.
Call Win^ methods to set the list window characteristics, e.g. colors,
title, remove status, boundaries, etc. Refer to chapter 7: Using Win-
dows for a thorough discussion of the StretchWinOBJ methods.
Note: the display colors used in list objects are controlled by
two different methods. The attributes of the list window perime-
ter, i.e. box, title, scroll bars and icons, are set using the
method:
Win^.SetColors.
The display attributes for the body of the window, i.e. the items
in the list, are controlled using the ListOBJ method SetColors,
which is discussed later in this section.
Listing Arrays
The ListArrayOBJ object provides an easy way to display a list based on
the contents of a string array. In addition to the common methods just
described, there is the following method:
AssignList(var StrArray;Total:longint;
StrLength:byte;Taggable:boolean);
AssignList identifies the string array that will be displayed. The four
parameters are the string array, the total number of elements in the
array, the length of each string in the array, and a boolean to indi-
cate whether individual items can be tagged. The string length parame-
ter must reflect the string length of the array when it was declared,
not the maximum length of any string assigned to the array.
Having assigned the list, all you need to do is call Show, and the
browse window will pop onto the display. Listed below is the demo pro-
gram DEMLS1.PAS.
9-22 User's Guide
--------------------------------------------------------------------------------
program DemoList1;
{demls1}
Uses DOS, CRT,
totFAST, totLIST;
Var
Items : array [1..20] of string[30];
ListWin: ListArrayObj;
procedure FillArray;
{}
begin
Items[1] := 'One';
Items[2] := 'Two';
Items[3] := 'Three';
Items[4] := 'Four';
Items[5] := 'Five';
Items[6] := 'Six';
Items[7] := 'Seven';
Items[8] := 'Eight';
Items[9] := 'Nine';
Items[10] := 'Ten';
Items[11] := 'Eleven';
Items[12] := 'Twelve';
Items[13] := 'Thirteen';
Items[14] := 'Fourteen';
Items[15] := 'Fifteen';
Items[16] := 'Sixteen';
Items[17] := 'Seventeen';
Items[18] := 'Eighteen';
Items[19] := 'Nineteen';
Items[20] := 'Twenty';
end; {FillArray}
begin
Screen.Clear(white,'░'); {paint the screen}
FillArray;
with ListWin do
begin
Init;
AssignList(Items,20,30,true);
Go;
Done;
end;
end.
This is a very basic list which uses all the default settings. Figure
9.9 illustrates the resultant display.
Managing Lists 9-23
--------------------------------------------------------------------------------
Figure 9.9 [SCREEN]
A Basic List
Window
The following few statements are an extract from the demo file
DEMLS2.PAS which is very similar to the previous demo, except that the
list window has been customized somewhat:
with ListWin do
begin
Init;
AssignList(Items,20,30,true);
SetColWidth(15);
Win^.SetTitle(' Pick a number! ');
Win^.SetSize(24,7,55,18,2);
Go;
Done;
end;
By calling the SetColWidth method, the list has been transformed from a
single list to a multi-column list. Refer back to figure 9.7 to see the
output generated from DEMLS2.PAS.
The program DEMLS3.PAS is yet another refinement of the array list
demo. In this on-disk file, the following statement is added to change
the list tagging and highlighting characters:
LookTOT^.SetListChars(chr(16),chr(17),chr(14),chr(32));
Listing Linked Lists
The ListLinkOBJ is very similar to the ListArrayOBJ. The primary dif-
ference is that ListLinkOBJ displays the data stored in a DLLOBJ object
rather than in an array.
In addition to the common methods described earlier, there are the
following two methods:
AssignList(var LinkList: DLLOBJ);
This method is passed a DLLOBJ instance, or any instance of an object
derived from DLLOBJ, e.g. StrDLLOBJ.
ListPtr: DLLPtr;
This method returns a pointer to the DLLOBJ used to create the list.
You can access the linked list by using the syntax MyList.ListPtr^.me-
thod.
9-24 User's Guide
--------------------------------------------------------------------------------
The demo program DEMLS4.PAS (listed below) shows how well StrDLLOBJ and
ListLinkOBJ work together. This small demo program creates an StrDLLOBJ
instance called ItemList. The list is populated by reading the contents
of an ASCII file. The ListLinkOBJ instance ListWin is then used to
display the file contents in a window. Figure 9.10 shows the generated
screen display.
program DemoList4;
{demls4 - reading a list from a text file}
Uses DOS, CRT,
totFAST, totLINK, totLIST;
Var
ListWin: ListLinkObj;
ItemList: StrDLLOBJ;
FileOK: boolean;
procedure LoadLinkedList;
{}
var
F: text;
Line:string;
Result: integer;
begin
with ItemList do
begin
Init;
{$I-}
Assign(F,'demls4.txt');
Reset(F);
{$I+}
FileOK := (IOResult = 0);
if not FileOK then
Result := Add('File not found')
else
begin
while not eof(F) do
begin
Readln(F,Line);
Result := Add(Line);
end;
close(F);
end;
end;
end; {LoadLinkedList}
Managing Lists 9-25
--------------------------------------------------------------------------------
begin
Screen.Clear(white,'░'); {paint the screen}
LoadLinkedList;
with ListWin do
begin
Init;
AssignList(ItemList);
SetColWidth(15);
Win^.SetTitle(' Items from file DEMLS4.TXT ');
Win^.SetSize(20,5,60,20,1);
if not FileOk then
SetTagging(false);
Go;
Done;
end;
ItemList.Done;
end.
Figure 9.10 [SCREEN]
Display a List
From a File
Determining Which Items are Tagged
Earlier you learned that by calling the method SetTagging(true), you
could allow the user to tag and untag items. The Toolkit provides the
method SetStatus to allow you to pre-tag (or, indeed, pre-untag!) items
before the list is displayed. Similarly, the method GetStatus is used
to determine the status of any item in the list.
Before explaining the syntax of these methods, we need to take a little
peek behind the scenes. The Toolkit stores eight different flags for
each item in the list. These flags are numbered 0 through 7. Flag 0 is
used to determine whether the user has tagged the item. Flag 1 indi-
cates which color to use in dual color mode (discussed later). The
remaining flags are not used and can be customized for descendant
objects. Refer to Part 2: Extending the Toolkit for more information.
The syntax of the SetStatus method is as follows:
SetStatus(Pick:longint; BitPos: byte; On:boolean);
To pre-tag an item, call the SetStatus method and pass three parame-
ters. The first parameter specifies the item to be modified, the second
parameter is the flag number (and this should be set to 0 (zero) to
modify the tag flag), and the third boolean parameter should be set to
True to tag the item, or False to un-tag it.
9-26 User's Guide
--------------------------------------------------------------------------------
After the user has removed the list window, and before the Done method
is called, the GetStatus method can be called to see which selections
the user made. The syntax of GetStatus is as follows:
GetStatus(Pick:longint; BitPos: byte): boolean;
This function method returns true if the identified item flag is set
on. The first parameter identifies the item number, and the second
parameter should be set to 0 (zero) to get the status of the tag flag.
The following demo program, DEMLS5.PAS, shows how to check for all the
tagged items. The program writes a list of all the tagged items.
program DemoList5;
{demls5 - selecting all tagged items}
Uses DOS, CRT,
totFAST, totLINK, totLIST;
Var
ListWin: ListLinkObj;
ItemList: StrDLLOBJ;
FileOK: boolean;
L,Total: longint;
procedure LoadLinkedList;
{}
var
F: text;
Line:string;
Result: integer;
begin
with ItemList do
begin
Init;
{$I-}
Assign(F,'demls4.txt');
Reset(F);
{$I+}
FileOK := (IOResult = 0);
if not FileOK then
Result := Add('File not found')
else
begin
while not eof(F) do
begin
Readln(F,Line);
Result := Add(Line);
end;
close(F);
Managing Lists 9-27
--------------------------------------------------------------------------------
end;
end;
end; {LoadLinkedList}
begin
Screen.Clear(white,'░'); {paint the screen}
LoadLinkedList;
with ListWin do
begin
Init;
AssignList(ItemList);
SetColWidth(15);
Win^.SetTitle(' Items from file DEMLS4.TXT ');
Win^.SetSize(20,5,60,20,1);
if not FileOk then
SetTagging(false);
Go;
Remove;
Total := ItemList.TotalNodes;
clrscr;
for L := 1 to Total do
if GetStatus(L,0) then
Writeln('Selected: ',GetString(L,0,0));
Done;
end;
ItemList.Done;
end.
In a real application, you might call a procedure to process the tagged
items, rather than simply write a list of all tagged items.
If you are displaying a linked list, you can take advantage of the
DLLOBJ method DelAllStatus. This method is used to automatically remove
entries from the list which have one of the status flags set in a
specified state. The syntax of the DLLOBJ method is as follows:
DelAllStatus(BitPos:byte; On:boolean);
This method removes all entries from the linked list which have the
specified flag set in the specified state. The first parameter indi-
cates the flag number, in the range 0 to 7. The second parameter deter-
mines whether all the flags set to True or False will be deleted. For
example, to remove all tagged entries in a linked list you would call
the method DelAllStatus(0,true);.
9-28 User's Guide
--------------------------------------------------------------------------------
Displaying Dual Colored Lists
All list objects are capable of displaying each item in one of two
color combinations. This facility is put to good effect in the ListDi-
rOBJ object (discussed in the next chapter), where the files are dis-
played in one color, and the subdirectories in another. The following
two methods impact the list display colors:
SetColors(HAttr,NAttr,SAttr: byte);
This method sets the display attributes for the items in the list. The
first parameter specifies the attribute of the active pick. The other
two attributes specify the attributes for the normal items. If the
status (discussed below) of the normal item is set to high, the SAttr
attribute will be used, otherwise the NAttr will be used.
SetDualColors(On:boolean);
If you want to exploit the dual color capabilities of the list objects,
pass True. Passing a False will force all the items to display in the
same color.
The Toolkit has to have some way of identifying the status of each
item, i.e. in which attribute the color should be displayed. The second
of the status flags, i.e. flag 1, is designated as the color flag. The
SetStatus and GetStatus flags are used to control the color feature, as
follows:
SetStatus(Pick:longint; BitPos: byte; On:boolean);
This method is used to set the status of any item in the list. The
first parameter identifies the number of the item to be set. The second
parameter should be set to 1 (one) to indicate that the color status
flag is being set. The final parameter is a boolean to indicate whether
status is on (True) or off (False).
GetStatus(Pick:longint; BitPos: byte): boolean;
This function method returns true if the identified item flag is set
on. The first parameter identifies the item number, and the second
parameter should be set to 1 (one) to determine the status of the dual
color flag.
An example of the dual color facility is included in the DEMLS7.PAS,
discussed in the Character Hook section at the end of the chapter.
Managing Lists 9-29
--------------------------------------------------------------------------------
Displaying Messages about the Highlighted Pick
The ListOBJ object family supports the optional display of a message at
the bottom of the list window. This message is normally related to the
currently highlighted pick. The message might be a long description of
a terse pick, or some other information about the highlighted pick.
There are two different ways to instruct the Toolkit to display a mes-
sage. The no-fuss way is to write a special procedure (called a message
hook) and instruct the Toolkit to call this procedure every time a pick
is highlighted. The alternative is to take advantage of an OOP feature
known as Polymorphism.
Once a message displaying routine has been implemented, it can be dis-
abled and enabled using the following method:
SetMsgState(On:boolean);
Pass True to activate the message display, or False to deactivate it.
Using a Message Hook
A message hook is an external function which is called every time a new
pick is highlighted. To utilize the message hook facility, all you have
to do is create a function following some specific rules, and then call
the method SetMsgHook to instruct the Toolkit to use your function.
For a function to be eligible as a message hook it must adhere to the
following rules:
Rule 1 The function must be declared as a FAR function. This can be
achieved by preceding the function with a {$F+} compiler
directive, and following the function with a {$F-} direc-
tive. Alternatively, Turbo 6 users can use the new keyword
FAR following the function statement.
Rule 2 The function must be declared with one passed parameter of
type longint. This parameter indicates the highlighted item
number.
Rule 3 The function must return a value of type string. This return
value is the actual text to be displayed in the message area
of the window display.
Rule 4 The function must be at the root level, i.e. the function
cannot be nested within another procedure or function.
The following function declaration follows these rules:
9-30 User's Guide
--------------------------------------------------------------------------------
{$F+}
function MyMessageHook(HiPick:longint): string;
begin
...{function statements}
MyMessageHook := 'something!';
end;
{$F-}
The following method SetMsgHook is then called to instruct the Toolkit
to call your function every time a new item is highlighted:
SetMsgHook(Func:ListMsgFunc);
This method is passed the function name of a function declared using
the rules outlined above, e.g. SetMsgHook(MyMessageHook);
The demo program DEMLS6 (listed below) implements a message hook. In
this case the message is simply a phrase stating which topic is high-
lighted. I hope your applications are a little more useful! Figure 9.11
illustrates the resultant output.
program DemoList6;
{demls6 - displaying a list message}
Uses DOS, CRT,
totFAST, totLINK, totLIST, totSTR, totMSG;
Var
ListWin: ListLinkObj;
ItemList: StrDLLOBJ;
FileOK: boolean;
{$F+}
function MsgHook(HiPick:longint):string;
{}
begin
MsgHook := 'The Hi Pick is '+IntToStr(HiPick);
end; {MsgHook}
{$F-}
procedure LoadLinkedList;
{}
var
F: text;
Line:string;
Result: integer;
begin
with ItemList do
Managing Lists 9-31
--------------------------------------------------------------------------------
begin
Init;
{$I-}
Assign(F,'demls4.txt');
Reset(F);
{$I+}
FileOK := (IOResult = 0);
if not FileOK then
Result := Add('File not found')
else
begin
while not eof(F) do
begin
Readln(F,Line);
Result := Add(Line);
end;
close(F);
end;
end;
end; {LoadLinkedList}
begin
Screen.Clear(white,'░'); {paint the screen}
LoadLinkedList;
with ListWin do
begin
Init;
AssignList(ItemList);
SetColWidth(15);
SetMsgHook(MsgHook);
Win^.SetTitle(' A List With Messages ');
Win^.SetSize(20,5,60,20,2);
Win^.SetMinSize(20,7);
if not FileOk then
SetTagging(false);
Go;
Done;
end;
ItemList.Done;
end.
Figure 9.11 [SCREEN]
A List with a Mes-
sage
9-32 User's Guide
--------------------------------------------------------------------------------
Creating a Descendant Object
By design, most of the documentation on customizing the Toolkit with
object oriented techniques is to be found in Part 2: Extending the
Toolkit. However, as an appetizer, this section explains how to take
advantage of OOP to implement your own message method, without needing
to pass a procedure as a parameter. The message hook procedure
described in the previous section is perfectly adequate and acceptable,
but it isn't OOP! If you want to learn a few OOP tidbits, read on,
otherwise be content with the message hook and skip to the next sec-
tion.
Usually, to customize a Toolkit object to better meet your needs, you
create a descendant object. For illustration, we will modify the List-
LinkOBJ object. Whenever you create a descendant object, you should
study the parent object's methods and decide which ones to replace or
modify.
A descendant object contains all the data and methods of its parent. As
a bare minimum, you should always define new Init and Done methods, and
then replace any other methods that you wish to improve or modify. (You
can also add new methods, but we'll leave that to Part 2!).
The ListLinkOBJ method includes the following virtual method:
function MessageTask(HiPick:longint):string; VIRTUAL;
This method is called every time a new topic is highlighted, and it
returns the text to be displayed. Since this method is declared VIR-
TUAL, we can replace the method in a descendant object, and other
methods will use the newly defined version of MessageTask.
The objective is to create a new object descendant from ListLinkOBJ,
and replace the MessageTask method with a new routine. The syntax for
declaring the new object type is as follows:
Type
NewListLinkOBJ = object (ListLinkOBJ)
constructor Init;
function MessageTask(HiPick:longint):string; VIRTUAL;
destructor Done; VIRTUAL;
end; {NewListLinkOBJ}
The descendant object will be called NewListLinkOBJ. Notice that the
special constructor and destructor procedures Init and Done are
declared, as well as the new MessageTask function. The constructor and
destructor methods are required when an object includes virtual meth-
ods, as it instructs Turbo Pascal to manage procedure calls differ-
ently. The keywords constructor and destructor are simply used as
replacements for procedure. The de facto OOP standard is to name the
constructor INIT and the destructor DONE.
Managing Lists 9-33
--------------------------------------------------------------------------------
If the ancestor method is declared VIRTUAL, the descendant method must
also be declared virtual. Furthermore, when you substitute virtual
methods in a descendant object, the declaration of the method must be
exactly the same as the ancestor. For this reason, MessageTask and Done
are both declared virtual, with the same parameters as ListLinkOBJ.
Having declared a new type NewListLinkOBJ, you must write the actual
object methods in the body of your program or unit. In this case, Turbo
Pascal expects the three object methods Init, MessageTask and Done.
Listed below is an example of how these methods might be written.
constructor NewListLinkOBJ.Init;
{}
begin
ListLinkOBJ.Init;
vMsgActive := true;
end; {NewListLinkOBJ.Init}
function NewListLinkOBJ.MessageTask(HiPick:longint):string;
{}
begin
MessageTask := 'The Hi Pick is '+IntToStr(HiPick);
end; {NewListLinkOBJ.MessageTask}
destructor NewListLinkOBJ.Done;
{}
begin
ListLinkOBJ.Done;
end; {NewListLinkOBJ.Done}
Notice that each method starts with the object name followed by a
period followed by the method identifier. Whenever you declare an Init
or Done method, you should always call the ancestors Init or Done --
very often, this ancestor method performs important data initialization
tasks, and let's not forget that a descendant object assumes all the
data of its ancestor.
In the new Init method, the variable vMsgActive is set to true. (All
object variables in the Toolkit commence with the letter "v".) This
variable indicates that the object should always call the message dis-
play method when the active topic is changed. In the LinkListOBJ, this
variable defaults to false, and is changed to true when the method
SetMsgHook is called. The main purpose of this descendant object is to
replace the old MessageTask method with a new one. In the new method,
the string value returned is just the statement identifying the high-
lighted topic.
That's it. Any instance of NewListLinkOBJ will automatically display
the string returned by the new MessageTask method. Listed below is the
example DEMLS7.PAS which shows the example in its entirety.
9-34 User's Guide
--------------------------------------------------------------------------------
program DemoList7;
{demls7 - creating a descendant ListObject}
Uses DOS, CRT,
totFAST, totLINK, totLIST, totSTR, totMSG;
Type
NewListLinkOBJ = object (ListLinkOBJ)
{Methods...}
Constructor Init;
function MessageTask(HiPick:longint):string; VIRTUAL;
destructor Done; VIRTUAL;
end; {NewListLinkOBJ}
Var
ListWin: NewListLinkObj;
ItemList: StrDLLOBJ;
FileOK: boolean;
{+++++new object methods+++++}
constructor NewListLinkOBJ.Init;
{}
begin
ListLinkOBJ.Init;
vMsgActive := true;
end; {NewListLinkOBJ.Init}
function NewListLinkOBJ.MessageTask(HiPick:longint):string;
{}
begin
MessageTask := 'The Hi Pick is '+IntToStr(HiPick);
end; {NewListLinkOBJ.MessageTask}
destructor NewListLinkOBJ.Done;
{}
begin
ListLinkOBJ.Done;
end; {NewListLinkOBJ.Done}
{+++++end of new object methods+++++}
procedure LoadLinkedList;
{}
var
F: text;
Line:string;
Result: integer;
begin
with ItemList do
begin
Init;
{$I-}
Managing Lists 9-35
--------------------------------------------------------------------------------
Assign(F,'demls4.txt');
Reset(F);
{$I+}
FileOK := (IOResult = 0);
if not FileOK then
Result := Add('File not found')
else
begin
while not eof(F) do
begin
Readln(F,Line);
Result := Add(Line);
end;
close(F);
end;
end;
end; {LoadLinkedList}
begin
Screen.Clear(white,'░'); {paint the screen}
LoadLinkedList;
with ListWin do
begin
Init;
AssignList(ItemList);
SetColWidth(15);
Win^.SetTitle(' A List With Messages ');
Win^.SetSize(20,5,60,20,2);
Win^.SetMinSize(20,7);
if not FileOk then
SetTagging(false);
Go;
Done;
end;
ItemList.Done;
end.
This example works just like DEMLS6.PAS, but a descendant object was
created rather than passing a procedure. I know what you are thinking
"It's a damn sight easier to pass a procedure!". That's true, and that
is precisely why the Toolkit includes the procedure passing alterna-
tive. By design, this first taste of OOP was a little simple. If your
curiosity has been piqued, review the source code in the unit
TOTLIST.PAS. You will see how the base object ListOBJ does all the
work, and how the descendant objects ListArrayOBJ and ListLinkOBJ make
the objects work for string arrays and linked lists. The totDIR unit
discussed in the next chapter extends the ListLinkOBJ and customizes it
specifically for displaying files and directories.
9-36 User's Guide
--------------------------------------------------------------------------------
Character Hooks
The ListOBJ object family provides ways to intercept every key pressed
by the user. This allows you to implement your own special hotkeys.
Like the Message facility described earlier, there are two different
ways to instruct the Toolkit to call a procedure every time a key is
pressed -- by passing a procedure, or by creating a descendant object.
Using a Character Hook
A character hook is an external procedure which is called every time a
key or mouse button is pressed. To utilize the character hook facility,
all you have to do is create a function following some specific rules,
and then call the method SetCharHook to instruct the Toolkit to use
your function.
For a function to be eligible as a character hook it must adhere to the
following rules:
Rule 1 The function must be declared as a FAR function. This can be
achieved by preceding the function with a {$F+} compiler
directive, and following the function with a {$F-} direc-
tive. Alternatively, Turbo 6 users can use the new keyword
FAR following the function statement.
Rule 2 The function must be declared with four (count 'em) passed
parameters. Parameter one must be a variable parameter of
type word. This parameter indicates which key the user just
pressed, and you may change the value of this parameter to
substitute a different key. The second and third parameters
must be variable parameters of type byte, and they represent
the X and Y coordinates of the mouse at the time the key was
pressed. The fourth parameter is non-variable, and must be
of type longint. This parameter indicates the highlighted
item number.
Rule 3 The function must return a value of type tListAction. This
is an enumerated type which indicates to the Toolkit how to
proceed. The members of the enumerated type are: Finish,
Refresh and None. If you want the list window to terminate,
return Finish. If you have changed, inserted, or deleted any
items in the visible list, return Refresh. The Toolkit will
then re-display the entire window contents. Normally, how-
ever, you will just return None.
Rule 4 The function must be at the root level, i.e. the function
cannot be nested within another procedure or function.
The following function declaration follows these rules:
Managing Lists 9-37
--------------------------------------------------------------------------------
{$F+}
function MyCharHook(var K:word;
var X,Y: byte;
HiPick:longint): tListAction;
begin
...{function statements}
MyCharHook := None;
end;
{$F-}
The following method SetCharHook is then called to instruct the Toolkit
to call your function every time a key is pressed:
SetCharHook(Func:ListCharFunc);
This method is passed the function name of a function declared using
the rules outlined above, e.g. SetCharHook(MyCharHook);.
The demo program DEMLS8.PAS, listed below, implements both a message
hook and a character hook. The character hook is checking for two spe-
cial keys. If [KEYCAP] is pressed, a simple help screen is displayed,
and if [KEYCAP] is pressed, the topic is changed to the alternative
color.
program DemoList8;
{demls8 - using Message and Character hooks}
Uses DOS, CRT,
totFAST, totLINK, totLIST, totSTR, totMSG;
Var
ListWin: ListLinkObj;
ItemList: StrDLLOBJ;
FileOK: boolean;
{$F+}
function HelpHook(var K:word; var X,Y: byte; HiPick:longint): tListAc-
tion;
{}
var MsgWin: MessageOBJ;
begin
HelpHook := None;
if K = 315 then
begin
with MsgWin do
begin
Init(6,'Kinda Help');
AddLine('');
AddLine('In a real application, this would');
9-38 User's Guide
--------------------------------------------------------------------------------
AddLine('be a help screen, and it would give');
AddLine('help related to item '+IntToStr(HiPick)+'!');
AddLine('');
Show;
Done;
end;
K := 0;
end
else if K = 316 then {F2 so swap colors}
begin
ListWin.SetStatus(HiPick,1,not ListWin.GetStatus(HiPick,1));
K := 336; {emulate down cursor}
end;
end; {HelpHook}
function MsgHook(HiPick:longint):string;
{}
begin
MsgHook := 'The Hi Pick is '+IntToStr(HiPick);
end; {MsgHook}
{$F-}
procedure LoadLinkedList;
{}
var
F: text;
Line:string;
Result: integer;
begin
with ItemList do
begin
Init;
{$I-}
Assign(F,'demls4.txt');
Reset(F);
{$I+}
FileOK := (IOResult = 0);
if not FileOK then
Result := Add('File not found')
else
begin
while not eof(F) do
begin
Readln(F,Line);
Result := Add(Line);
end;
close(F);
Managing Lists 9-39
--------------------------------------------------------------------------------
end;
end;
end; {LoadLinkedList}
begin
Screen.Clear(white,'░'); {paint the screen}
Screen.WriteCenter(25,white,' F1 Help F2 Toggle Color! [Space]
Toggle Tag ');
LoadLinkedList;
with ListWin do
begin
Init;
AssignList(ItemList);
SetColWidth(15);
SetCharHook(HelpHook);
SetMsgHook(MsgHook);
SetDualColors(true);
Win^.SetTitle(' A Multi-Colored List ');
Win^.SetSize(20,5,60,20,2);
Win^.SetMinSize(20,7);
if not FileOk then
SetTagging(false);
Go;
Done;
end;
ItemList.Done;
end.
Figure 9.12 [SCREEN]
Using a
Character Hook
Creating a Descendant Object
I think we've had enough OOP for one chapter. Suffice it to say that
just like the message hook, the character hook can be implemented by
creating a descendant object and replacing a ListLinkOBJ method called
CharTask.
The demo file DEMLS9.PAS (listed below) shows how to replace both the
character hook and the message hook by creating a new descendant
object.
program DemoListNine;
{demls9 - extending the LinkListOBJ object}
Uses DOS, CRT,
totFAST, totLINK, totLIST, totSTR, totMSG;
9-40 User's Guide
--------------------------------------------------------------------------------
Type
NewListLinkOBJ = object (ListLinkOBJ)
{Methods...}
Constructor Init;
function MessageTask(HiPick:longint):string; VIRTUAL;
function CharTask(var K:word; var X,Y: byte;
HiPick:longint): tListAction; VIRTUAL;
destructor Done; VIRTUAL;
end; {NewListLinkOBJ}
Var
ListWin: NewListLinkObj;
ItemList: StrDLLOBJ;
FileOK: boolean;
{+++++new object methods+++++}
constructor NewListLinkOBJ.Init;
{}
begin
ListLinkOBJ.Init;
vMsgActive := true;
end; {NewListLinkOBJ.Init}
function NewListLinkOBJ.MessageTask(HiPick:longint):string;
{}
begin
MessageTask := 'The Hi Pick is '+IntToStr(HiPick);
end; {NewListLinkOBJ.MessageTask}
function NewListLinkOBJ.CharTask(var K:word; var X,Y: byte;
HiPick:longint): tListAction;
{}
var MsgWin: MessageOBJ;
begin
CharTask := none;
if K = 315 then
begin
with MsgWin do
begin
Init(6,'Kinda Help');
AddLine('');
AddLine('In a real application, this would');
AddLine('be a help screen, and it would give');
AddLine('help related to item '+IntToStr(HiPick)+'!');
AddLine('');
Show;
Done;
end;
K := 0;
Managing Lists 9-41
--------------------------------------------------------------------------------
end
else if K = 316 then {F2 so swap colors}
begin
ListWin.SetStatus(HiPick,1,not ListWin.GetStatus(HiPick,1));
K := 336; {emulate down cursor}
end;
end; {NewListLinkOBJ.CharTask}
destructor NewListLinkOBJ.Done;
{}
begin
ListLinkOBJ.Done;
end; {NewListLinkOBJ.Done}
{+++++end of new object methods+++++}
procedure LoadLinkedList;
{}
var
F: text;
Line:string;
Result: integer;
begin
with ItemList do
begin
Init;
{$I-}
Assign(F,'demls4.txt');
Reset(F);
{$I+}
FileOK := (IOResult = 0);
if not FileOK then
Result := Add('File not found')
else
begin
while not eof(F) do
begin
Readln(F,Line);
Result := Add(Line);
end;
close(F);
end;
end;
end; {LoadLinkedList}
begin
Screen.Clear(white,'░'); {paint the screen}
Screen.WriteCenter(25,white,' F1 Help F2 Toggle Color! [Space]
Toggle Tag ');
LoadLinkedList;
9-42 User's Guide
--------------------------------------------------------------------------------
with ListWin do
begin
Init;
AssignList(ItemList);
SetColWidth(15);
SetDualColors(true);
Win^.SetTitle(' A Multi-Colored List ');
Win^.SetSize(20,5,60,20,2);
Win^.SetMinSize(20,7);
if not FileOk then
SetTagging(false);
Go;
Done;
end;
ItemList.Done;
end.